package com.company.dcm.utils;

import java.util.Date;

public class IdUtils {
    // 基准时间 2021-1-1
    private final long START_TIME = 1609430400000L;
    // 机器ID
    private final long WorkerId=1L;
    
    // 机器标识位数
    private final int WORKER_BITS = 8;
    // 时钟序列标识位数
    private final int CLOCK_BITS = 2;
    // 序列号标识位数
    private final int SEQUENCE_BITS = 12;

    // 机器ID最大值
    private final long MAX_WORKER = ~(-1L << WORKER_BITS);
    // 时钟序列最大值
    private final long MAX_CLOCK = ~(-1L << CLOCK_BITS);
    // 序列号ID最大值
    private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    // 机器ID偏左移位
    private final int WORKER_SHIFT = SEQUENCE_BITS;
    // 时钟序列偏左移位
    private final int CLOCK_SHIFT = SEQUENCE_BITS + WORKER_BITS;
    // 时间毫秒左移位
    private final int TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_BITS + CLOCK_BITS;
    
    // 时钟序列
    private long clock = 0L;
    
    // 序列号ID
    private long sequence = 0L;
    
    // 最后时间戳
    private long lastTimestamp = -1L;

    //单例模式
    public static IdUtils idUtils;

    public IdUtils(){
    }

    //单例模式
    public static IdUtils getInstance(){
        if(idUtils == null){
            synchronized (IdUtils.class){
                idUtils = new IdUtils();
            }
        }
        return idUtils;
    }

    //获取基于起始时间的时间戳
    public long getTime() {
        return (new Date().getTime())-START_TIME;
    }

    // 获取下一个ID
    public synchronized long NextId() {
        //当前系统时间戳
        long currentTimestamp = getTime();
        //出现时间回拨 当前系统时间小于最后更新时间
        if (currentTimestamp < lastTimestamp) {
            // clock自增，和MAX_CLOCK相与一下，去掉高位
            clock = (clock + 1L) & MAX_CLOCK;
        }
        // 如果上次生成时间和当前时间相同，在同一毫秒内
        if (lastTimestamp == currentTimestamp) {
            // sequence自增，和MAX_SEQUENCE相与一下，去掉高位
            sequence = (sequence + 1L) & MAX_SEQUENCE;
            //判断是否溢出,也就是每毫秒内超过4096
            if (sequence == 0L) {
                //等待到下一毫秒
                currentTimestamp = getNextMillis(lastTimestamp);
            }
        } else {
            //如果和上次生成时间不同,重置sequence，就是下一毫秒开始，sequence计数重新从0开始累加,
            sequence = 0L;
        }
        lastTimestamp = currentTimestamp;
        return ((currentTimestamp) << TIMESTAMP_SHIFT) | clock << CLOCK_SHIFT | (WorkerId << WORKER_SHIFT) | sequence;

    }

    // 获取增量时间戳，防止产生的时间比之前的时间还要小（由于NTP回拨等问题），保持增量的趋势
    private long getNextMillis(long lastTimestamp) {
        long timestamp = getTime();
        while (timestamp <= lastTimestamp) {
            timestamp = getTime();
        }
        return timestamp;
    }
}
